﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using ChatData;
using System.Threading;
using System.Net.Sockets;
using System.IO;
using Server;
namespace Client
{
    /// <summary>
    /// base for all Controls in client
    /// </summary>
    public partial class ClientBase : Form
    {
        ChatServerClient client;
        string username;
        string status;
        DateTime lastrefresh;
        int port = Client.Properties.Settings.Default.DefaultPort;
        TcpListener tcp;

        public ClientBase(ChatServerClient theclient)
        {
            InitializeComponent();
            client = theclient;
            if (port == 8080 || port == 8000)
            {
                port = 7000;
            }
            tcp = new TcpListener(Utils.GetIPAddress(), port);
            setEvents();
            if (Login() == true)
            {
                lastrefresh = DateTime.Now;
                tcp.Start();
                this.backgroundWorker1.RunWorkerAsync(tcp);
                edit1.Activeusers = new ActiveUserStore<Server.Record> (client.getActiveUsers());
                edit1.User = username;
                edit1.Status = "Avaliable";
                edit1.Client = client;
                this.entryList1.LoggedIn(username);
                client.UpdateStatus(username, "Avaliable");
                Reset();
                RefreshTimer.Enabled = true;
            }
            else
            {
                Application.Exit();
            }
        }
        /// <summary>
        /// Set up EntryList event handlers
        /// </summary>
        private void setEvents()
        {
            this.entryList1.PostEvent += new PostEventhandler(entryList1_PostEvent);
            this.entryList1.DMPostEvent += new DMPostEventhandler(entryList1_DMPostEvent);
            this.entryList1.FileForwardEvent += new FileEventhandler(entryList1_FileForwardEvent);
            this.entryList1.FileReplyEvent += new FileReplyEventhandler(entryList1_FileReplyEvent);
            this.entryList1.FileSaveEvent += new FileSaveEventhandler(entryList1_FileSaveEvent);
            this.entryList1.StatusChangeEvent += new StatusChangedEventhandler(entryList1_StatusChangeEvent);
        }
        /// <summary>
        /// User Login
        /// </summary>
        private bool Login()
        {
                string password;
                if (Client.Properties.Settings.Default.UserName != "")
                {
                    username = Client.Properties.Settings.Default.UserName;
                    password = Client.Properties.Settings.Default.Password;
                    bool result =client.Login(Utils.GetLocalIPAddress(), port.ToString(), username, password); // password is already in base64encoded
                    if (result != false)
                    {
                        return true;
                    }
                    if (ShowLoginDialog() != false)
                    {
                        return true;
                    }
                    return false;
                }
                else
                {
                    if (ShowLoginDialog() != false)
                    {
                        return true;
                    }
                    return false;
                }
        }
        /// <summary>
        /// Show User Login Dialog Box
        /// </summary>
        private bool ShowLoginDialog()
        {
             using (Client.Login dialog = new Login())
            {
            System.Windows.Forms.DialogResult result = dialog.ShowDialog();
            username = dialog.Username();
            switch (result)
            {
                case DialogResult.Yes:
                    return client.Login(Utils.GetLocalIPAddress(), port.ToString(), username, Utils.EncodePasswordToBase64(dialog.Password()));
                    break;
                case DialogResult.Cancel:
                    return false;
                    break;
            }
            return false;
            }
        }
        #region Handle Response
        /// <summary>
        /// Handle the TCP Response from the server
        /// </summary>
        private void HandleServerResponse(ReturnArgs theargs)
        {
            string temp="";
            String tempargs = "";
            foreach(KeyValuePair<String,String> item in theargs){
                temp = item.Key;
                tempargs = item.Value;
            }
            switch (temp)
            {
                case "DM":
                    Reset();
                    lastrefresh = DateTime.Now;
                    break;
                case"File":
                    HandleFile(client.RequestFile(username, tempargs));
                    break;
                case "Status":
                    this.edit1.Activeusers = new ActiveUserStore<Record>(client.getActiveUsers());
                    string status = edit1.Activeusers.getStatus(tempargs);
                    this.entryList1.DisplayMessage(tempargs + "is  " +status);
                    break;
                case "Login":
                    this.edit1.Activeusers = new ActiveUserStore<Record>(client.getActiveUsers());
                    this.entryList1.DisplayMessage(tempargs + " has logged in");
                    break;
                case "Logout":
                    this.edit1.Activeusers = new ActiveUserStore<Record>(client.getActiveUsers());
                    this.entryList1.DisplayMessage(tempargs + " has logged out");
                    break;
            }
        }
        /// <summary>
        /// Handle File Response from the server
        /// </summary>
        private void HandleFile(FilePost tempfile)
        {
            String path;
                if (Client.Properties.Settings.Default.DefaultFilePath == "")
                {
                     path = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), tempfile.LocalFile);
                }
                else
                {
                    path = System.IO.Path.Combine(Client.Properties.Settings.Default.DefaultFilePath, tempfile.LocalFile);
                }
                Array.Reverse(tempfile.File);//reset to original byte order;
            System.IO.File.WriteAllBytes(path, tempfile.File);
            tempfile.LocalFile = path;
            tempfile.File = null;// free up memory
            this.entryList1.addFile(tempfile);
        }
        #endregion
        /// <summary>
        /// Update user Status
        /// </summary>
        private void UpdateStatus()
        {
            client.UpdateStatus(this.username, this.status);
        }
        /// <summary>
        /// Populate EntryList with updated Post and DMPost items
        /// </summary>
        private void Reset()
        {
            List<Post> newfeed = new List<Post>();
            List<DMPost> newDMS = new List<DMPost>();
            try
            {
               newfeed = client.getUpdatedStream(username, lastrefresh).ToList();
            }
            catch (Exception e)
            {

            }
            try
            {
              newDMS = client.getDMStreamSince(username, lastrefresh).ToList();
            }
            catch (Exception e)
            {

            }
            this.entryList1.addPostFeed(newfeed,newDMS);
            lastrefresh = DateTime.Now;
        }
        /// <summary>
        /// Handle EntryList FileSave event
        /// </summary>
        private void HandleFileSave(String from, String to)
        {
            //FileInfo info = new FileInfo(from);
            //info.MoveTo(to);

            try
            {
                System.IO.File.Move(from, to);
            }
            catch (IOException e)
            {
                MessageBox.Show("A file with that name already exits. Please choose a different directory to save your file to");
            }
        }
        /// <summary>
        /// Handle EntryList FilefForward event
        /// </summary>
        private void FowardFile(DataEventArgs<FilePost> eventargs)
        {
            string Destination = edit1.getDestination();
            client.ForwardFile(username, Destination, eventargs.Data.GUID);
        }
        #region Eventhandlers
        private void RefreshTimer_Tick(object sender, EventArgs e)
        {
            Reset();
        }
        /// <summary>
        /// Setup Edit for a Reply
        /// </summary>
        void entryList1_FileReplyEvent(object e, ReplyChatEventArgs<FilePost> eventargs)
        {
            this.edit1.Reply(eventargs.Data.User);
        }
        //final call
        void entryList1_FileForwardEvent(object e, DataEventArgs<FilePost> eventargs)
        {
            this.FowardFile(eventargs);
        }
        void entryList1_FileSaveEvent(object e, FileSaveEventArgs<FilePost> eventargs)
        {
            this.HandleFileSave(eventargs.Data.LocalFile, eventargs.Destination);
        }

        void entryList1_DMPostEvent(object e, ReplyChatEventArgs<DMPost> eventargs)
        {
            this.edit1.Reply(eventargs.Data);
        }

        void entryList1_PostEvent(object e, DataEventArgs<Post> eventargs)
        {
            this.edit1.Reply(eventargs.Data.User);
        }

        void entryList1_StatusChangeEvent(object e, DataEventArgs<string> eventargs)
        {
            this.status = eventargs.Data;
            UpdateStatus();
        }
        #endregion

        #region Background Thread
        // the folowing code adapted from http://msdn.microsoft.com/en-us/library/hybbz6ke.aspx
        // and SquashServer lab
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {

            BackgroundWorker bw = sender as BackgroundWorker;

            // Extract the argument.
            TcpListener arg = (TcpListener)e.Argument;

            e.Result = RunTCP(bw, arg);

            // If the operation was canceled by the user, 
            // set the DoWorkEventArgs.Cancel property to true.
            if (bw.CancellationPending)
            {
                e.Cancel = true;
            }
        }
        private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                
            }
            else if (e.Error != null)
            {
                throw new Exception(e.Error.Message, e.Error);
            }
            else
            {
                TcpClient theclient = (TcpClient)e.Result;
                Stream newstream = theclient.GetStream();
                try
                {
                    this.HandleServerResponse(Process.Message(newstream));
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                this.backgroundWorker1.RunWorkerAsync(tcp);// restart the thread for the next message
            }
        }
        private TcpClient RunTCP( BackgroundWorker bw, TcpListener listener)
        {
            TcpClient client = new TcpClient();
            while (!bw.CancellationPending)
            {

                for (; ; )
                {
                        client = tcp.AcceptTcpClient();
                        return client;
                }

            }
            return null;
        }



        #endregion

        /// <summary>
        /// Logout user before closing
        /// </summary>
        private void ClientBase_FormClosing(object sender, FormClosingEventArgs e)
        {
            client.Logout(username);
        }

        private void entryList1_StatusChangeEvent(object e)
        {

        }

        private void entryList1_FileSaveEvent(object e)
        {

        }
    }
}
